﻿using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace HttpWebServer
{
    public sealed class ServerSide
    {
        private TcpListener _tcpListener;
        private TcpClient _tcpClient;
        private bool _isActive;
        private readonly int _clientBuffer;
        private readonly string _hostName;
        private readonly int _port;
        private readonly ManualResetEvent _tcpClientConnected = new ManualResetEvent(false);
        public event EventHandler<MessageEventArgs> ClientRequestMessageEvent;
        public event EventHandler<MessageEventArgs> ClientResponseMessageEvent;

        public ServerSide(string hostName, int port, int clientBuffer)
        {
            Trace.WriteLine(DateTime.Now + "-->" + "Method ServerSide initialized");
            _clientBuffer = clientBuffer;
            _hostName = hostName;
            _port = port;
        }

        ~ServerSide()
        {
            Trace.WriteLine(DateTime.Now + "-->" + "Method ~ServerSide initialized");
            ShutdownServerSide();
        }

        public void StartServer()
        {
            IPAddress ip;
            if (!IPAddress.TryParse(_hostName, out ip))
            {
                Trace.TraceError(DateTime.Now + "-->" + "Start Server error. Couldn't parse host");
                return;
            }
            _tcpListener = new TcpListener(ip, _port);
            _tcpListener.Start();
            _isActive = true;
            while (_isActive)
            {
                try
                {
                    var cThread = new Thread(ClientThread);
                    //ThreadPool.QueueUserWorkItem(new WaitCallback(ClientThread), tcpListener.AcceptTcpClient());
                    _tcpClientConnected.Reset();
                    _tcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), _tcpListener);
                    _tcpClientConnected.WaitOne();
                    cThread.Start(_tcpClient);
                }
                catch { return; }
                Thread.Sleep(10);
            }
        }

        private void DoAcceptTcpClientCallback(IAsyncResult ar)
        {
            var listener = (TcpListener) ar.AsyncState;
            try
            {
                _tcpClient = listener.EndAcceptTcpClient(ar);
            }
            catch (Exception)
            {
            }
            _tcpClientConnected.Set();
        }

        public void ShutdownServerSide()
        {
            Trace.TraceInformation(DateTime.Now + "-->" + "Method ShutdownServerSide initialized");
            _isActive = false;
            _tcpClientConnected.Set();
            Thread.Sleep(10);
            if (_tcpListener == null) return;
            _tcpListener.Stop();
            _tcpListener = null;
            Trace.TraceInformation(DateTime.Now + "-->" + "Server stopped");
        }

        private void ClientThread(Object stateInfo)
        {
            Trace.TraceInformation(DateTime.Now + "-->" + "Client thread started");
            //ClientSide clientSide = new ClientSide((TcpClient)StateInfo, clientBuffer);
            //clientSide.RequestMessageEvent += new EventHandler<MessageEventArgs>(clientSide_RequestMessageEvent);
            //clientSide.StartClientSide();
            var clientSide = new ClientSide();
            clientSide.RequestMessageEvent += ClientSideRequestMessageEvent;
            clientSide.ResponseMessageEvent += ClientSideResponseMessageEvent;
            clientSide.StartClientSide((TcpClient)stateInfo, _clientBuffer);
        }

        private void ClientSideResponseMessageEvent(object sender, MessageEventArgs e)
        {
            OnClientResponseMessageEvent(e);
        }

        private void ClientSideRequestMessageEvent(object sender, MessageEventArgs e)
        {
            OnClientRequestMessageEvent(e);
        }

        private void OnClientRequestMessageEvent(MessageEventArgs args)
        {
            Trace.TraceInformation(DateTime.Now + "-->" + "Client request event raised");
            Trace.TraceInformation(DateTime.Now + "-->" + "Message length: " + args.Message.Length);
            var handler = ClientRequestMessageEvent;
            if (handler != null)
            {
                handler(this, args);
            }
        }

        private void OnClientResponseMessageEvent(MessageEventArgs args)
        {
            Trace.TraceInformation(DateTime.Now + "-->" + "Client response event raised");
            Trace.TraceInformation(DateTime.Now + "-->" + "Message length: " + args.Message.Length);
            var handler = ClientResponseMessageEvent;
            if (handler != null)
            {
                handler(this, args);
            }
        }
    }
}
